# Copyright (C) 2009, Mathias Gyllengahm
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

import sys
import time
import traceback
from dtypes import WIIMOTE, NUNCHUK, BALANCE
from logfilehandler import LogfileHandler

TRIGGERVAL = 200
FALLOFFMAX = 10
NEXTNOTECOUNT = 10

IDLE = 0
RAISING = 1
RAISED = 2
LOWERING = 3
POSTHIT = 4
STATEDESCR = ['IDLE', 'RAISING', 'RAISED', 'LOWERING', 'POSTHIT' ]

class BalanceBoardState:
    def __init__(self):
        self.rtt = 0
        self.ltt = 0
        self.retrig = 7

    def process(self, data):
        diff = data[1] - self.rtt
        self.rtt = data[1]
        self.ltt = data[3]
        if diff > 50 and diff < 400 and self.retrig == 0:
            self.retrig = 7 
            print "hit!" + str(diff)
            return [ 0, diff ]
        if self.retrig > 0:
            self.retrig = self.retrig - 1
        return [ -1, -1 ]


class State:
    def __init__(self, triggerlow = 130, triggerhigh = 170):
        self.ztrack = -1
        #self.zlow =  255
        #self.zhigh = 0
        self.falloffcounter = 0
        self.nextnotecounter = NEXTNOTECOUNT + 1
        self.state = IDLE
        self.zmin = -1
        self.zmax = -1
        self.triggerlow = triggerlow
        self.triggerhigh = triggerhigh

    def switchstate(self, state):
        self.state = state
        #print STATEDESCR[self.state]

    def process(self, z):
        retval = [-1 , -1]
        if self.state == IDLE:
            if z < self.triggerlow:
                self.switchstate(RAISING)

        if self.state == RAISING:
            if z <= self.ztrack or self.ztrack == -1:
                self.ztrack = z
                self.zmin = -1
            elif z > self.ztrack:
                # We've found a min
                self.zmin = self.ztrack
                self.switchstate(RAISED)
                self.falloffcounter = 0
                self.ztrack = -1
        if self.state == RAISED:
            if z > self.triggerhigh:
                #self.switchstate(LOWERING)
                retval = [self.zmin, z ]
                self.zmax = self.ztrack
                #print "HIT! " + str(retval)
                self.switchstate(POSTHIT)
                self.nextnotecounter = 0
                self.zmax = -1 
                self.zmin = -1 
                self.ztrack = -1
            else:
                self.falloffcounter = self.falloffcounter + 1
            if self.falloffcounter > FALLOFFMAX:
                self.switchstate(IDLE) # There was just an 'up'-movement, no beat down
                self.falloffcounter = 0
                self.zmin = -1
        if self.state == POSTHIT:
            self.nextnotecounter = self.nextnotecounter + 1
            if self.nextnotecounter > NEXTNOTECOUNT:
                self.switchstate(IDLE)
        return retval
"""
# Working but too long delay on outnotes
class State:
    def __init__(self, triggerlow = 130, triggerhigh = 170):
        self.ztrack = -1
        #self.zlow =  255
        #self.zhigh = 0
        self.falloffcounter = 0
        self.nextnotecounter = NEXTNOTECOUNT + 1
        self.state = IDLE
        self.zmin = -1
        self.zmax = -1
        self.triggerlow = triggerlow
        self.triggerhigh = triggerhigh

    def switchstate(self, state):
        self.state = state
        print STATEDESCR[self.state]

    def process(self, z):
        retval = [-1 , -1]
        if self.state == IDLE:
            if z < self.triggerlow:
                self.switchstate(RAISING)

        if self.state == RAISING:
            if z <= self.ztrack or self.ztrack == -1:
                self.ztrack = z
                self.zmin = -1
            elif z > self.ztrack:
                # We've found a min
                self.zmin = self.ztrack
                self.switchstate(RAISED)
                self.falloffcounter = 0
                self.ztrack = -1
        if self.state == RAISED:
            if z > self.triggerhigh:
                self.switchstate(LOWERING)
                self.ztrack = -1
            else:
                self.falloffcounter = self.falloffcounter + 1
            if self.falloffcounter > FALLOFFMAX:
                self.switchstate(IDLE) # There was just an 'up'-movement, no beat down
                self.falloffcounter = 0
                self.zmin = -1
        if self.state == LOWERING:
            if z > self.ztrack:
                self.ztrack = z
            else:
                if self.ztrack > self.triggerhigh:
                    self.zmax = self.ztrack
                    retval = [self.zmin, self.zmax ]
                    self.zmax = self.ztrack
                    #print "HIT! " + str(retval)
                    self.switchstate(POSTHIT)
                    self.nextnotecounter = 0
                    self.zmax = -1 
                    self.zmin = -1 
                    self.ztrack = -1
        if self.state == POSTHIT:
            self.nextnotecounter = self.nextnotecounter + 1
            if self.nextnotecounter > NEXTNOTECOUNT:
                self.switchstate(IDLE)
        return retval
"""
#        retval = 0
#        if z > TRIGGERVAL and self.nextnotecounter > NEXTNOTECOUNT:
#            if z > self.ztrack:
#                self.ztrack = z
#                #print "newminz:" + str(zstat['minz'])
#
#        if self.ztrack != 0:
#            # We're looking for extremest value
#            self.falloffcounter = self.falloffcounter + 1
#            if self.falloffcounter > FALLOFFMAX:
#                # Trigger note!
#                #print "Trigger!"
#                retval = self.ztrack
#                self.ztrack = 0
#                self.falloffcounter = 0
#
#        if self.nextnotecounter < NEXTNOTECOUNT:
#            self.nextnotecounter = self.nextnotecounter + 1
#        return retval

class Processor:
    def __init__(self):
        self.motestate    = State()
        self.chukstate    = State()
        self.balancestate = BalanceBoardState()
        
    def process(self, type, data):
        if type == WIIMOTE:
            z = data[2]
            r = self.motestate.process(z)
            #print "MOTE: " + str(z) + " : " + str(r)
            return r
        elif type == NUNCHUK:
            z = data[2]
            r = self.chukstate.process(z)
            #print "CHUK: " + str(z) + " : " + str(r)
            return r
        elif type == BALANCE:
            r = self.balancestate.process(data)
            return r


"""
TRIGGERVAL = 130
FALLOFFMAX = 10
NEXTNOTECOUNT = 30
class Processor:
    def __init__(self):
        self.mzstat = {}
        self.mzstat['minz'] = 255
        self.mzstat['falloffcounter'] = 0
        self.mzstat['nextnotecounter'] = NEXTNOTECOUNT + 1
        self.nzstat = {}
        self.nzstat['minz'] = 255
        self.nzstat['falloffcounter'] = 0
        self.nzstat['nextnotecounter'] = NEXTNOTECOUNT + 1
        
    def detecthit(self, z, zstat):
        trig = ""
        retval = 0
        if z < TRIGGERVAL and zstat['nextnotecounter'] > NEXTNOTECOUNT:
            if z < zstat['minz']:
                zstat['minz'] = z
                #print "newminz:" + str(zstat['minz'])

        if zstat['minz'] != 255:
            # We're looking for extremest value
            zstat['falloffcounter'] = zstat['falloffcounter'] + 1
            if zstat['falloffcounter'] > FALLOFFMAX:
                # Trigger note!
                #print "Trigger!"
                retval = zstat['minz']
                zstat['minz'] = 255
                zstat['falloffcounter'] = 0

        if zstat['nextnotecounter'] < NEXTNOTECOUNT:
            zstat['nextnotecounter'] = zstat['nextnotecounter'] + 1
        return retval
    
    def process(self, type, data):
        if type == WIIMOTE:
            z = data[2]
            r = self.detecthit(z, self.mzstat)
            #print "MOTE: " + str(z) + " : " + str(r)
            return r
        elif type == NUNCHUK:
            z = data[2]
            r = self.detecthit(z, self.nzstat)
            #print "CHUK: " + str(z) + " : " + str(r)
            return r
"""



#import mytest
def main():
    processor = Processor()
    logfilehandler = mytest.LogfileHandler(sys.argv[1])
    logfilehandler.replayfile(processor.process, ignoreTime=True)
    try:
        while True:
            time.sleep(0.2)
    except:
        traceback.print_exc(file=sys.stdout)
        sys.exit(1)

if __name__ == '__main__':
    main()

